package web

import (
	"commerce/cache"
	"commerce/common"
	"commerce/data"
	"commerce/utils"
	"context"
	"encoding/json"
	"fmt"
	jwt "github.com/dgrijalva/jwt-go"
	"net/http"
	"time"
)

const (
	APP = "appUser" // applet 会员
)

// applet 会员用户

type AppUser struct {
	Mid       int    // 会员id
	Mnickname string // 会员昵称
	Avatar    string // 会员头像

	OpenId     string
	SessionKey string
}

func generateAppJWT(memberId int, nickname string) (string, error) {

	t := jwt.New(jwt.SigningMethodHS256)

	t.Claims = jwt.MapClaims{
		// issuer
		"iss":        "ss",
		"memberInfo": AppUser{Mid: memberId, Mnickname: nickname},
		"exp":        time.Now().Add(common.APP_TOKEN_AGE * time.Hour).Unix(),
	}
	tokenstring, err := t.SignedString(common.GetVK())
	if err != nil {
		return "", err
	}
	return tokenstring, nil
}
func AppAuthorize(w http.ResponseWriter, r *http.Request, next http.HandlerFunc) {

	tokenHeader := r.Header["Token"]
	if tokenHeader == nil || len(tokenHeader) == 0 {
		common.DisplayTokenErr(w, http.StatusUnauthorized)
		return
	}
	tokenStr := tokenHeader[0]

	token, err := jwt.Parse(tokenStr, func(token *jwt.Token) (interface{}, error) {
		return common.GetVK(), nil
	})
	if err != nil {
		switch err.(type) {

		case *jwt.ValidationError:
			vErr := err.(*jwt.ValidationError)
			switch vErr.Errors {
			case jwt.ValidationErrorExpired:
				common.DisplayTokenErr(w, http.StatusUnauthorized)
				return
			default:
				common.DisplayTokenErr(w, http.StatusUnauthorized)
				return
			}
		default:
			common.DisplayTokenErr(w, http.StatusUnauthorized)
			return
		}
	}

	if token.Valid {
		// 解析token, 提取用户信息, 查询redis缓存，验证redis中的token是否和当前token一致
		mapClaims, ok := token.Claims.(jwt.MapClaims)
		if !ok || mapClaims == nil {
			common.DisplayTokenErr(w, http.StatusUnauthorized)
			return
		}
		memberMap, ok := mapClaims["memberInfo"].(map[string]interface{})
		if !ok {
			common.DisplayTokenErr(w, http.StatusUnauthorized)
			return
		}
		mid := int(memberMap["Mid"].(float64))
		key := fmt.Sprintf("%s:%d", "Member", mid)
		cacheToken, err := cache.Get(key)
		if err != nil || cacheToken != token.Raw {
			common.DisplayTokenErr(w, http.StatusUnauthorized)
			return
		}
		member, err := data.GetMemberById(mid)
		if err != nil || len(member.OpenId) == 0 || len(member.SessionKey) == 0 {
			common.DisplayTokenErr(w, http.StatusUnauthorized)
		}
		//把当前合法app用户 保存在上下文中
		r = r.WithContext(context.WithValue(context.Background(),
			//APP, &AppUser{Mid: mid, Mnickname: memberMap["Mnickname"].(string)}))
			APP, &AppUser{Mid: mid, Mnickname: member.NickName, Avatar: member.Avatar}))

		next(w, r)
	} else {
		common.DisplayTokenErr(w, http.StatusUnauthorized)
	}
}

func parseAppToken(tok string) (*AppUser, error) {

	token, err := jwt.Parse(tok, func(token *jwt.Token) (interface{}, error) {
		return common.GetVK(), nil
	})
	if err != nil {
		return nil, err
	}
	if !token.Valid {
		return nil, fmt.Errorf("invalid app token")
	}
	switch token.Claims.(type) {
	case jwt.MapClaims:
		ui := token.Claims.(jwt.MapClaims)["memberInfo"]
		bytes, _ := json.Marshal(ui)
		var jwtUser AppUser
		err := json.Unmarshal(bytes, &jwtUser)
		if err != nil {
			return nil, err
		}
		cacheToken, err := cache.Get(fmt.Sprintf("%s:%d", "Member", jwtUser.Mid))
		if err != nil {
			return nil, err
		}
		if cacheToken != token.Raw {
			return nil, fmt.Errorf("invalid app token input:%s", tok)
		}
		return &jwtUser, nil
	default:
		return nil, fmt.Errorf("invalid app claim:%s", tok)
	}
}

// 登录凭证校验
func codeToSession(appId string, secret string, code string) (wxRsp CodeToSessionResp, err error) {

	url := fmt.Sprintf("https://api.weixin.qq.com/sns/jscode2session?appid=%s&secret=%s&js_code=%s&grant_type=authorization_code", appId, secret, code)
	body, err := utils.HttpGet(url)
	if err != nil {
		return
	}
	err = json.Unmarshal(body, &wxRsp)
	return
}

type CodeToSessionResp struct {
	OpenId     string `json:"openid"`      // 用户唯一标识
	SessionKey string `json:"session_key"` // 会话密钥
	UnionId    string `json:"unionid"`     // 只有在用户将公众号绑定到微信开放平台帐号后，才会出现该字段。
	ErrCode    int    `json:"errcode"`     // 错误码
	ErrMsg     string `json:"errmsg"`      // 错误信息
}
